﻿/****************************************************************
*   作者：Morain
*   创建时间：2018/2/8 21:47:01
*   描述说明：
*****************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Model
{
  [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
  public class ObjectSystemAttribute : Attribute { }

  /// <summary>
  /// 事件系统接口
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public interface IObjectSystem
  {
    Type Type();
    void Set(object obj);
  }

  /// <summary>
  /// 对象系统
  /// </summary>
  /// <typeparam name="T"></typeparam>
  public abstract class ObjectSystem<T> : IObjectSystem
  {
    private T value;

    protected T Self
    {
      get
      {
        return value;
      }
    }

    protected T Get()
    {
      return value;
    }

    public void Set(object obj)
    {
      this.value = (T)obj;
    }

    public Type Type()
    {
      return typeof(T);
    }
  }

  public enum AssemblyType
  {
    Hotfix,
    Model
  }

  /// <summary>
  /// 事件系统
  /// </summary>
  public class EventSystem
  {
    // 一个组件类型，对于一个对象系统。
    private Dictionary<Type, IObjectSystem> objectSystems = new Dictionary<Type, IObjectSystem>();
    // 程序集
    private Dictionary<AssemblyType, Assembly> assemblys = new Dictionary<AssemblyType, Assembly>();

    private Queue<AObject> starts = new Queue<AObject>();

    private List<AObject> updates = new List<AObject>();

    public Assembly[] GetAllAssembly()
    {
      return this.assemblys.Values.ToArray();
    }

    /// <summary>
    /// 添加程序集
    /// </summary>
    /// <param name="assemblyType"></param>
    public void Add(AssemblyType assemblyType, Assembly assembly)
    {
      assemblys[assemblyType] = assembly;

      Type[] types = assembly.GetTypes();
      foreach (var type in types)
      {
        ObjectSystemAttribute osa = type.GetCustomAttribute<ObjectSystemAttribute>(false);
        if (osa == null) continue;

        IObjectSystem objectSystem = Activator.CreateInstance(type) as IObjectSystem;
        if (objectSystem != null)
          objectSystems[objectSystem.Type()] = objectSystem;
        else
          Log.Error("创建objectSystem错误！" + type.Name);
      }
    }

    /// <summary>
    /// 添加一个对象
    /// </summary>
    /// <param name="obj"></param>
    public void Add(AObject obj)
    {
      if (!this.objectSystems.TryGetValue(obj.GetType(), out IObjectSystem objectSystem)) return;

      if (objectSystem is IStart)
        this.starts.Enqueue(obj);

      if (objectSystem is IUpdate)
        this.updates.Add(obj);
    }

    /// <summary>
    /// Awake唤醒
    /// </summary>
    /// <param name="obj"></param>
    public void Awake(AObject obj)
    {
      this.Add(obj);

      Type type = obj.GetType();

      if (!this.objectSystems.TryGetValue(type, out IObjectSystem objectSystem)) return;

      IAwake awake = objectSystem as IAwake;
      if (awake == null) return;
      objectSystem.Set(obj);
      awake.Awake();
    }

    public void Awake<P>(AObject obj, P t)
    {

      this.Add(obj);

      Type type = obj.GetType();

      if (!this.objectSystems.TryGetValue(type, out IObjectSystem objectSystem)) return;

      IAwake<P> awake = objectSystem as IAwake<P>;
      if (awake == null) return;

      objectSystem.Set(obj);
      awake.Awake(t);
    }

    public void Awake<P1, P2>(AObject obj, P1 p1, P2 p2)
    {

      this.Add(obj);

      Type type = obj.GetType();

      if (!this.objectSystems.TryGetValue(type, out IObjectSystem objectSystem)) return;

      IAwake<P1, P2> awake = objectSystem as IAwake<P1, P2>;
      if (awake == null) return;

      objectSystem.Set(obj);
      awake.Awake(p1, p2);
    }

    public void Start()
    {
      while (this.starts.Count > 0)
      {
        AObject obj = this.starts.Dequeue();

        if (!this.objectSystems.TryGetValue(obj.GetType(), out IObjectSystem objectSystem)) return;

        IStart iStart = objectSystem as IStart;

        if (iStart == null) return;
        objectSystem.Set(obj);
        iStart.Start();
      }
    }

    public void Update()
    {
      this.Start();
      foreach (AObject obj in this.updates)
      {
        if (!this.objectSystems.TryGetValue(obj.GetType(), out IObjectSystem objectSystem)) continue;

        IUpdate iUpdate = objectSystem as IUpdate;

        if (iUpdate == null) continue;

        objectSystem.Set(obj);

        iUpdate.Update();
      }
    }
  }
}